use std::fs::File;
use std::io;
use std::path::Path;

use matroska::MatroskaError;

use crate::util::dimensions::DimensionsExtractor;
use crate::util::Dimensions;

pub struct MkvDimensionsExtractor;

impl DimensionsExtractor for MkvDimensionsExtractor {
    fn supports_ext(&self, ext_lowercase: &str) -> bool {
        "mkv" == ext_lowercase || "webm" == ext_lowercase
    }

    fn try_read_dimensions(&self, path: &Path) -> io::Result<Option<Dimensions>> {
        let fd = File::open(path)?;
        let matroska = matroska::Matroska::open(fd).map_err(|err| match err {
            MatroskaError::Io(io) => io,
            MatroskaError::UTF8(utf8) => io::Error::new(io::ErrorKind::InvalidData, utf8),
            e => io::Error::new(io::ErrorKind::InvalidData, e),
        })?;
        Ok(matroska
            .tracks
            .iter()
            .find(|&track| track.tracktype == matroska::Tracktype::Video)
            .and_then(|ref track| {
                if let matroska::Settings::Video(settings) = &track.settings {
                    Some(Dimensions {
                        width: settings.pixel_width as usize,
                        height: settings.pixel_height as usize,
                    })
                } else {
                    None
                }
            }))
    }
}

#[cfg(test)]
mod test {
    use super::MkvDimensionsExtractor;
    use crate::util::dimensions::{test::test_successful, Dimensions};
    use std::error::Error;

    #[test]
    fn test_success() -> Result<(), Box<dyn Error>> {
        test_successful(
            MkvDimensionsExtractor,
            "video/rust-logo-blk.mkv",
            Some(Dimensions {
                width: 144,
                height: 144,
            }),
        )
    }
}
